/********************************************************************************
 * @file    app_adc.c
 * @author  jianqiang.xue
 * @version V1.0.0
 * @date    2021-04-10
 * @brief   ADC采集, 根据电阻特性和外部电路计算
 ********************************************************************************/

/* Includes ------------------------------------------------------------------*/
#include <math.h>
#include <stdint.h>

#include "app_adc.h"
#include "bsp_adc.h"
#include "log.h"
#include "os_api.h"
/* Private Includes ----------------------------------------------------------*/
#include "app_main.h"
#include "ls_syscfg.h"
#include "ls_gpio.h"

#define TIMER_ADC_COLLECTION_PERIOD LS_ADC0_SAMPLING_TIMES

/* Private Includes ----------------------------------------------------------*/
#define CALCULATE_NTC_RES_VAL(adc_val)       (adc_val * LS_THERMISTOR_PULL_UP_VAL) / ((LS_THERMISTOR_REFERENCE_VOLTAGE / 1000.0) * \
                                             (1 - (adc_val / (LS_THERMISTOR_REFERENCE_VOLTAGE / 1000.0))))

#define CALCULATE_TEMPERATURE_VALUE(abc_val) (1.0 / ((log(CALCULATE_NTC_RES_VAL(abc_val) / 100)) / LS_THERMISTOR_NTC_K + 1.0 / (273.15 + 25.0)) - 273.15)

/* Private Variables ---------------------------------------------------------*/
#if LS_ADC0_EN
static void timer_adc0(void const *arg);
static os_timer_id adc0_id = NULL;
os_timer_def(adc0, timer_adc0);
#endif

/* Private Function Prototypes -----------------------------------------------*/
#if LS_ADC0_EN
/*
 * @brief  [软定时器回调函数] adc周期性采集
 * @note   利用定时器周期性采集adc各个通道
 */
static void timer_adc0_cb(void const* arg) {
    // 启动ADC中断转换 将ADC0的通道各采集一遍
    bsp_adc0_start();
}

/**
 * @brief  [ADC采集完毕中断] 发送信号量给主任务
 */
static void adc0_callback(void) {
    main_send_signal(SIGNAL_ADC_COLLECTION);
}
#endif

/* Public Function Prototypes ------------------------------------------------*/

/**
 * @brief  ADC电压转换实际电压值
 * @param  adc_v_val ADC转换后的电压值. 使用值(mv) = 真实值(v) *1000.
 * @return 实际电压值(mv)
 */
uint16_t get_external_vcc(uint16_t adc_v_val) {
    return (adc_v_val * (LS_POWER_PULL_UP_VAL + LS_POWER_PULL_DOWN_VAL) /
            LS_POWER_PULL_DOWN_VAL);
}

/**
 * @brief  将热敏电阻的电压转换为温度值
 * @param  adc_v_val ADC转换后的电压值. 使用值(mv)=实际值(v)*100
 * @return 温度值 (*100)
 */
int16_t get_external_temp(uint16_t adc_v_val) {
    return CALCULATE_TEMPERATURE_VALUE(adc_v_val / 1000.0) * 100;
}

/**
 * @brief  将LDR的电压转换为电阻值
 * @param  adc_v_val ADC转换后的电压值。 使用值(mv)=实际值(v)*1000
 * @return LDR value (*1000)
 */
uint16_t get_external_ldr(uint16_t adc_v_val) {
    return (adc_v_val * LS_LDR_PULL_UP_VAL) /
           (LS_LDR_REFERENCE_VOLTAGE *
            (1.0 - (adc_v_val / LS_LDR_REFERENCE_VOLTAGE)));
}

#if LS_ADC0_EN
/**
 * @brief  [ADC0] 开启扫描
 * @retval 0--失败 1--成功
 */
bool app_adc0_scan_start(void) {
    if (os_timer_start(adc0_id, TIMER_ADC_COLLECTION_PERIOD) != OS_OK) {
        LOG_E("<ERR> adc0_scan timer start fail\r\n");
        return false;
    }
    return true;
}

/**
 * @brief  [ADC0] 停止扫描
 * @retval 0--失败 1--成功
 */
bool app_adc0_scan_stop(void) {
    if (os_timer_stop(adc0_id) != OS_OK) {
        LOG_E("<ERR> adc0_scan timer stop fail\r\n");
        return false;
    }
    return true;
}
#endif

/**
 * @brief  [ADC初始化] 软定时器创建
 * @retval 0--失败 1--成功
 */
bool app_adc_init(void) {
#if LS_ADC0_EN
    uint32_t timer_ret;
    // 注册ADC0采集完毕回调
    bsp_adc0_init();
    bsp_adc0_irq_callback(adc0_callback);
    // 创建软定时器扫描adc通道(等系统开启完毕后，再启动，警告这里启动定时器会导致os崩溃)
    adc0_id = os_timer_create(os_timer(adc0), OS_TIMER_PERIODIC, &timer_ret);
    if (!adc0_id) {
        LOG_E("<ERR> adc0_id timer create fail\r\n");
        return false;
    }
#endif
    return true;
}

void app_adc_deinit(void) {
#if LS_ADC0_EN
    app_adc0_scan_stop();
    bsp_adc0_deinit();
#endif
}
